”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 在 Spring Boot 中创建自定义注释的终极指南

在 Spring Boot 中创建自定义注释的终极指南

发布于2024-11-05
浏览:728

The Ultimate Guide to Create Custom Annotations in Spring Boot
Such annotations fill the entire project in Spring Boot.

But do you know what problems these annotations solve?

Why were custom annotations introduced to begin with?

How to create custom annotations?

Today, I will cover:

  • Why create custom annotations?
  • What are the key benefits of using these annotations?
  • How to create custom annotations?
  • How does the annotated method get invoked?
  • When to use custom annotations?
  • When not to use custom annotations?
  • What are the disadvantages of using custom annotations?

? Why Create Custom Annotations?

In Spring Boot, annotations are more than just a way to add metadata. They

  • Simplify complex tasks
  • Reduce boiler-plate code
  • Enhance code-readability

Before Spring introduced custom annotations, developers had to manage configurations like email validation using XML configuration files.

The XML configuration would define beans, validators, and other necessary components to perform tasks such as validating email addresses.

Here's an example of how email validation might have been configured using XML in a Spring application:

The Ultimate Guide to Create Custom Annotations in Spring Boot

As you can see, this can easily become a nightmare where there are hundreds of classes with many of them relying on each other.

It also meant a developer had to go look up this XML every time they had to add a new dependency.

Key Benefits of Custom Annotations

Simplification of Configuration

Spring introduced custom annotations to simplify configuration by allowing developers to use annotations directly in their code.

This reduced the need for extensive XML configuration, making the codebase cleaner and easier to maintain.

Support for Declarative Programming

Custom annotations in Spring enable a declarative approach.

Developers can use annotations like @Transactional, @Cacheable, or @Scheduled to declare desired behaviors without writing the underlying logic.

This results in more readable and maintainable code.

Handling Cross-Cutting Concerns

Spring's custom annotations, often used with Aspect-Oriented Programming (AOP), allow developers to handle cross-cutting concerns in a centralized manner.

For example, the @Transactional annotation manages transactions across multiple methods or classes without scattering transaction management logic throughout the code.

Reducing Boilerplate Code

It reduces the need for boilerplate code by encapsulating common behaviours.

For instance, the @Autowired annotation simplifies dependency injection, allowing Spring to automatically inject dependencies, rather than requiring explicit constructor or setter methods

It is a different discussion whether you should be using @Autowired or not.

Improving Code Readability and Consistency

By abstracting configuration and cross-cutting concerns into annotations, Spring improves the readability of the code.

You and your peer developers can quickly understand the purpose of a method or class by looking at its annotations, and annotations help enforce consistency across the codebase.

Framework Flexibility and Extensibility

Custom annotations allow developers to create their annotations tailored to specific needs, thus extending the framework's functionality in a standardized way.

This flexibility has helped Spring remain relevant and powerful across multiple applications and architectures.

? How to Create a Custom Annotation

Step 1: Define the Annotation

  • Create a new annotation by defining an interface.
  • Use @interface to declare it.
  • Add meta-annotations to specify how the annotation should behave.
package co.officegeek.tokenratelimiter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)  // Annotation available at runtime
@Target(ElementType.METHOD)          // Can be applied to methods
public @interface LogExecutionTime {
}
  • @Target: Indicates where the annotation can be used (e.g., methods, classes).
  • @Retention: Indicates how long the annotation is retained (e.g., runtime, compile-time).

Step 2: Create an Aspect to Handle the Annotation

You can create a custom logic to process the annotation using Spring's BeanPostProcessor, Aspect, or custom annotation processing logic.

package co.officegeek.tokenratelimiter;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogExecutionTimeAspect {

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        System.out.println(joinPoint.getSignature()   " executed in "   executionTime   "ms");
        return proceed;
    }
}

Step 3: Apply the Annotation

Apply your custom annotation to methods, fields, or classes as defined.

package co.officegeek.tokenratelimiter;

import org.springframework.stereotype.Service;

@Service
public class TestService {

    @LogExecutionTime
    public void serve() throws InterruptedException {
        // Simulate some work
        Thread.sleep(2000);
    }
}

How It Works:

  • The @LogExecutionTime annotation doesn't cause any method to be called directly.
  • The Spring AOP framework detects that a method has the @LogExecutionTime annotation using reflection.
  • The LogExecutionTimeAspect aspect is configured to apply around advice when a method with the @LogExecutionTime annotation is called.
  • The logExecutionTime method in the aspect is executed before and after the annotated method (serve), logging the execution time.

The Ultimate Guide to Create Custom Annotations in Spring Boot


How does the annotated method get invoked?

When you apply a custom annotation to a method, class, or field, the annotation itself doesn't directly cause any method to be called.

Instead, the logic associated with the annotation is typically implemented using reflection or aspect-oriented programming (AOP) in frameworks like Spring.

Here's a breakdown of how the compiler and runtime environment know what method to call when an annotation is applied:

1. Compile-Time Processing (Annotation Processors)

Some annotations are handled at compile time by annotation processors.

Java's javax.annotation.processing package allows developers to create custom annotation processors that generate code, validate annotations, or even modify the abstract syntax tree (AST) of the code being compiled.

The annotation processor reads the annotations during compilation and executes code based on those annotations.

This can include generating new classes or methods that the code will use later.

The @Override annotation is a compile-time annotation that doesn't invoke a method but instead tells the compiler to check if the method actually overrides a superclass method.

How It Works:

  • You define a custom annotation processor by extending AbstractProcessor and overriding the process method.
  • The processor will be invoked by the compiler when it encounters your annotation, allowing you to generate code or perform other tasks.

2. Runtime Processing (Reflection)

Custom annotations can be processed at runtime using reflection.

The runtime system (e.g., a framework like Spring) uses reflection to detect the presence of annotations on methods, classes, or fields, and then applies the corresponding behavior.

A custom annotation like @LogExecutionTime doesn't directly trigger any method call.

Instead, an aspect or some other reflective mechanism checks for the presence of the annotation at runtime and then wraps the method call with additional logic.

How It Works:

  • At runtime, you use Java's reflection API to check if a method or class has a specific annotation using methods like isAnnotationPresent.
  • Once detected, you can invoke methods or execute logic associated with that annotation.  For example, if a method has a @LogExecutionTime annotation, you might measure the time before and after the method call.

3. Aspect-Oriented Programming (AOP)

In frameworks like Spring, AOP is commonly used to handle custom annotations.

AOP allows you to define "aspects" that can intercept method calls and perform additional processing before or after the method execution.

When the AOP framework (e.g. Spring AOP) detects an annotation, it triggers the execution of an advice method associated with the aspect.

This advice method contains the logic that the AOP framework executes when the annotated method is called.

A @Transactional annotation in Spring doesn't execute any logic by itself.

Instead, the Spring framework's AOP infrastructure intercepts calls to methods annotated with @Transactional and wraps them with transaction management logic.

How It Works:

  • You define an aspect class with advice methods that are associated with specific pointcuts (join points where you want to apply the advice).
  • The aspect uses annotations like @Around or @Before to specify when the advice should be executed.
  • The AOP framework ensures that when a method with a custom annotation is called, the corresponding advice is executed automatically.

Use Cases Where Custom Annotations Are a Good Approach

Cross-Cutting Concerns

Custom annotations are ideal for handling cross-cutting concerns like logging, security, transaction management, and caching.

These are concerns that affect multiple parts of an application but are not related to the core business logic.

The @LogExecutionTime annotation above is a good example as that can be used across all the methods and it does not have any business logic.

Declarative Programming

When you want to specify what should happen rather than how it should happen, custom annotations provide a clean and expressive way to do this.

Annotations like @Cacheable or @Retry allow developers to enable caching or retry logic declaratively, without writing the implementation code manually.

Framework or Library Integration

Custom annotations can simplify the integration of frameworks or libraries by hiding the complexity behind an easy-to-use annotation.

Annotations like @Autowired in Spring help in injecting dependencies without having to manually instantiate them.

Encapsulation of Complex Logic

When complex logic needs to be encapsulated in a reusable way, custom annotations can provide a clean API for applying this logic.

An annotation like @RateLimit could encapsulate logic to limit the number of times a method can be called, without cluttering the method's body with this logic.

Use Cases Where Custom Annotations Should Not Be Used

Simple or One-Off Logic

If the logic is simple or only needs to be applied in a single place, creating a custom annotation is overkill and can unnecessarily complicate the code.

Logic That Requires Dynamic Behavior

Annotations are statically defined at compile-time, making them unsuitable for scenarios where behaviour needs to be dynamically determined at runtime.

If a method's behaviour should change based on user input or external configuration, handling this with custom annotations can lead to complex solutions.

Business Logic

Core business logic should not be abstracted into custom annotations, as this can make the logic less transparent and harder to maintain.

Using an annotation to encapsulate a business process like @ProcessOrder might hide important business rules, making the code harder to understand and maintain.

Complex Interactions Between Annotations

If the behavior depends on complex interactions between multiple annotations, it can lead to unexpected results and make the code difficult to understand and debug.

Combining multiple custom annotations that affect the same method (e.g., @Retry, @Cacheable, @LogExecutionTime) can result in unpredictable behavior and is difficult to manage

Performance-Critical Code

Custom annotations often rely on reflection or proxy mechanisms, which can introduce performance overhead.

They should not be used in performance-critical sections of code.

Using a custom annotation to add logging to a method that is called millions of times in a tight loop could significantly degrade performance.

? Summary - When to Use Custom Annotations

Custom annotations are perfect for handling cross-cutting concerns like logging, security, and transaction management.

They're also great for scenarios where you need to apply the same behaviour across multiple parts of your application.

However, for simple, one-off logic, or where fine-grained control and flexibility are required, custom annotations might not be the best approach.

Consider the trade-offs before you decide to implement them.

? Final Thoughts

Custom annotations are a powerful tool in your Spring Boot arsenal, but like any tool, they should be used judiciously.

They offer a clean, reusable way to handle repetitive tasks and enforce consistency across your codebase.

But be mindful of the potential downsides, especially for complexity and performance.


?? Announcement

I am launching a 10-day cohort-based course for software developers and aspiring microservices architects on designing and implementing rate-limiting service using Spring Boot and Bucket4j.

You'll learn:

✅ How to design and build a production-ready microservice

✅ In-depth knowledge of rate-limiting algorithms and their implementation

✅ Best practices in Spring Boot development, testing, and containerisation

But it is also about

✅ breaking down the project into specific tasks

✅ Being accountable to yourself

✅ Designing and Building the project right

It is targeted at software developers who want to design and develop a microservice which is a use case relevant to most companies.

It's ESPECIALLY for those earlier in their software developer career who might not have "project experience" but tons of passion and ambition.

If this is something that you think will help you or even if you are just curious to know more:

Register your interest and I will let you know the workshop details.


This was first published on my Substack. Subscribe to my Substack - Weekend Developer to get updates first.

Are you a developer who needs feedback on the code you write?

Or do you want someone to review your code so that you are doing the right things?

I help people with free code review sessions so that they can get early feedback and write better code

DM me on Twitter (X) or on LinkedIn and I will help you with your code.

版本声明 本文转载于:https://dev.to/dishitdevasia/the-ultimate-guide-to-create-custom-annotations-in-spring-boot-fpa?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何解决“TypeError:\'str\'对象不支持项目分配”错误?
    如何解决“TypeError:\'str\'对象不支持项目分配”错误?
    'str'对象项分配错误疑难解答尝试在 Python 中修改字符串中的特定字符时,您可能会遇到错误“类型错误:“str”对象不支持项目分配。”发生这种情况是因为 Python 中的字符串是不可变的,这意味着它们无法就地更改。解决此问题的一种常见方法是将字符串转换为可变列表,进行必要的...
    编程 发布于2024-11-05
  • 如何缓解 GenAI 代码和 LLM 集成中的安全问题
    如何缓解 GenAI 代码和 LLM 集成中的安全问题
    GitHub Copilot and other AI coding tools have transformed how we write code and promise a leap in developer productivity. But they also introduce new ...
    编程 发布于2024-11-05
  • Spring 中的 ContextLoaderListener:必要的邪恶还是不必要的复杂?
    Spring 中的 ContextLoaderListener:必要的邪恶还是不必要的复杂?
    ContextLoaderListener:必要的邪恶还是不必要的复杂?开发人员经常遇到在 Spring Web 应用程序中使用 ContextLoaderListener 和 DispatcherServlet。然而,一个令人烦恼的问题出现了:为什么不简单地使用 DispatcherServlet...
    编程 发布于2024-11-05
  • JavaScript 机器学习入门:TensorFlow.js 初学者指南
    JavaScript 机器学习入门:TensorFlow.js 初学者指南
    机器学习 (ML) 迅速改变了软件开发世界。直到最近,得益于 TensorFlow 和 PyTorch 等库,Python 仍是 ML 领域的主导语言。但随着 TensorFlow.js 的兴起,JavaScript 开发人员现在可以深入令人兴奋的机器学习世界,使用熟悉的语法直接在浏览器或 Node...
    编程 发布于2024-11-05
  • extjs API 查询参数示例
    extjs API 查询参数示例
    API 查询 参数是附加到 API 请求 URL 的键值对,用于向服务器发送附加信息。它们允许客户端(例如 Web 浏览器或应用程序)在向服务器发出请求时指定某些条件或传递数据。 查询参数添加到 URL 末尾问号 (?) 后。每个参数都是一个键值对,键和值之间用等号 (=) 分隔。如果有多个查询参数...
    编程 发布于2024-11-05
  • 如何解决Go中从不同包导入Proto文件时出现“Missing Method Protoreflect”错误?
    如何解决Go中从不同包导入Proto文件时出现“Missing Method Protoreflect”错误?
    如何从不同的包导入 Proto 文件而不遇到“Missing Method Protoreflect”错误在 Go 中,protobuf 常用于数据序列化。将 protobuf 组织到不同的包中时,可能会遇到与缺少 ProtoReflect 方法相关的错误。当尝试将数据解组到单独包中定义的自定义 p...
    编程 发布于2024-11-05
  • 为什么MySQL在查询“Field = 0”非数字数据时返回所有行?
    为什么MySQL在查询“Field = 0”非数字数据时返回所有行?
    不明确的查询:理解为什么 MySQL 返回“Field=0”的所有行在 MySQL 查询领域,一个看似无害的比较,例如“SELECT * FROM table WHERE email=0”,可能会产生意外的结果。它没有按预期过滤特定行,而是返回表中的所有记录,从而引发了对数据安全性和查询完整性的担忧...
    编程 发布于2024-11-05
  • 服务器发送事件 (SSE) 的工作原理
    服务器发送事件 (SSE) 的工作原理
    SSE(服务器发送事件)在 Web 开发领域并未广泛使用,本文将深入探讨 SSE 是什么、它是如何工作的以及它如何受益您的申请。 什么是上交所? SSE 是一种通过 HTTP 连接从服务器向客户端发送实时更新的简单而有效的方法。它是 HTML5 规范的一部分,并受到所有现代 Web ...
    编程 发布于2024-11-05
  • 如何从字符串 TraceID 创建 OpenTelemetry Span?
    如何从字符串 TraceID 创建 OpenTelemetry Span?
    从字符串 TraceID 构造 OpenTelemetry Span要建立 Span 之间的父子关系,必须在上下文传播不可行的情况下使用标头。在这种情况下,跟踪 ID 和跨度 ID 包含在消息代理的标头中,这允许订阅者使用父跟踪 ID 创建新的跨度。解决方案以下步骤可以使用跟踪 ID 在订阅者端构建...
    编程 发布于2024-11-05
  • 如何在gRPC中实现服务器到客户端的广播?
    如何在gRPC中实现服务器到客户端的广播?
    gRPC 中的广播:服务器到客户端通信建立 gRPC 连接时,通常需要将事件或更新从服务器广播到客户端连接的客户端。为了实现这一点,可以采用各种方法。Stream Observables一种常见的方法是利用服务器端流。每个连接的客户端都与服务器建立自己的流。然而,直接订阅其他服务器客户端流是不可行的...
    编程 发布于2024-11-05
  • 为什么填充在 Safari 和 IE 选择列表中不起作用?
    为什么填充在 Safari 和 IE 选择列表中不起作用?
    在 Safari 和 IE 的选择列表中不显示填充尽管 W3 规范中没有限制,但 WebKit 浏览器不支持选择框中的填充,包括Safari 和 Chrome。因此,这些浏览器中不应用填充。要解决此问题,请考虑使用 text-indent 而不是 padding-left。通过相应增加选择框的宽度来...
    编程 发布于2024-11-05
  • 在 Spring Boot 中创建自定义注释的终极指南
    在 Spring Boot 中创建自定义注释的终极指南
    Such annotations fill the entire project in Spring Boot. But do you know what problems these annotations solve? Why were custom annotations introduce...
    编程 发布于2024-11-05
  • 为什么 Elixir 在异步处理方面比 Node.js 更好?
    为什么 Elixir 在异步处理方面比 Node.js 更好?
    简单回答:Node.js 是单线程的,并拆分该单线程来模拟并发,而 Elixir 利用了 Erlang 虚拟机 BEAM 原生的并发和并行性,同时执行进程。 下面,我们将更深入地了解这种差异,探索两个关键概念:Node.js 事件循环和 Elixir 的 BEAM VM 和 OTP。这些元素对于理解...
    编程 发布于2024-11-05
  • AngularJS $watch 如何替代动态导航高度调整中的计时器?
    AngularJS $watch 如何替代动态导航高度调整中的计时器?
    避免 AngularJS 的高度监视计时器当导航高度是动态时,AngularJS 程序员经常面临响应式导航的挑战。这就导致需要调整内容的 margin-top 值以响应导航高度的变化。以前,使用计时器来检测导航高度的变化,但这种方法有缺点:使用计时器和调整内容的 margin-top 出现延迟。幸运...
    编程 发布于2024-11-05
  • 从零到 Web 开发人员:掌握 PHP 基础知识
    从零到 Web 开发人员:掌握 PHP 基础知识
    掌握PHP基础知识至关重要:安装PHP创建PHP文件运行代码理解变量和数据类型使用表达式和运算符创建实际项目以提高技能PHP开发入门:掌握PHP基础PHP是一种用途广泛、功能强大的脚本语言,用于创建动态且交互式Web应用程序。对于初学者来说,掌握PHP的基本知识至关重要。一、安装PHP在本地开发机器...
    编程 发布于2024-11-05

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3